package com.yth.utils;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2025/7/7 20:42
 */
public class ParamHelper<T> {
    private static final ParamHelper<?> EMPTY = new ParamHelper<>(null);

    private final T value;

    public static <T> ParamHelper<T> of(T param) {
        if (param == null) {
            return empty();
        }
        return new ParamHelper<>(param);
    }

    public static <T> ParamHelper<T> empty() {
        //noinspection unchecked
        return (ParamHelper<T>) EMPTY;
    }

    private ParamHelper(T value) {
        this.value = value;
    }

    public <U> ParamHelper<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (this.value != null) {
            return ParamHelper.of(mapper.apply(value));
        }
        return empty();
    }

    public <U> ParamHelper<U> flatMap(Function<? super T, ParamHelper<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (this.value != null) {
            return mapper.apply(value);
        }
        return empty();
    }

    public ParamHelper<T> filter(Predicate<T> predicate) {
        Objects.requireNonNull(predicate);
        if (this.value != null) {
            return predicate.test(value) ? this : empty();
        }
        return this;
    }

    public <E extends Enum<E>> ParamHelper<E> parseEnum(Class<E> enumClass) {
        Objects.requireNonNull(enumClass);
        if (this.value instanceof String) {
            E enumValue = parseEnum((String) this.value, enumClass);
            return ParamHelper.of(enumValue);
        }
        return empty();
    }

    public ParamHelper<T> ifPresent(Runnable runnable) {
        Objects.requireNonNull(runnable);
        if (value != null) {
            runnable.run();
        }
        return this;
    }

    public ParamHelper<T> ifPresent(Consumer<? super T> consumer) {
        Objects.requireNonNull(consumer);
        if (value != null) {
            consumer.accept(value);
        }
        return this;
    }

    public ParamHelper<T> ifAbsent(Runnable runnable) {
        Objects.requireNonNull(runnable);
        if (value == null) {
            runnable.run();
        }
        return this;
    }

    public ParamHelper<T> ifAbsentThrow(Supplier<RuntimeException> supplier) {
        Objects.requireNonNull(supplier);
        if (value == null) {
            throw supplier.get();
        }
        return this;
    }

    public boolean isAbsent() {
        return value == null;
    }

    public boolean isPresent() {
        return value != null;
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public T orElseNull() {
        return value;
    }

    public T orElse(T defaultValue) {
        return value != null ? value : defaultValue;
    }

    public T orElseGet(Supplier<T> supplier) {
        Objects.requireNonNull(supplier);
        return value != null ? value : supplier.get();
    }

    public T orElseThrow(Supplier<RuntimeException> supplier) {
        Objects.requireNonNull(supplier);
        if (value == null) {
            throw supplier.get();
        }
        return value;
    }

    private <E extends Enum<E>> E parseEnum(String value, Class<E> enumClass) {
        for (E enumConstant : enumClass.getEnumConstants()) {
            if (value.equals(enumConstant.name())) {
                return enumConstant;
            }
        }
        return null;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof ParamHelper)) {
            return false;
        }

        ParamHelper<?> other = (ParamHelper<?>) obj;
        return Objects.equals(value, other.value);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    @Override
    public String toString() {
        return value != null
                ? String.format("ParamHelper[%s]", value)
                : "ParamHelper.empty";
    }
}
